import java.util.*;

class Bone
{
   private int id;
   private String name;
   private double[] direction;
   private double[] currentDir;

   private double length;
   private double[] axis;
   private boolean motion;
   private String dof;
   private double[][] limits;
   public Bone parent = null;
   public ArrayList<Bone> children = null;

   public float scale = 1.0f;

   boolean root;
   String order;
   String rootAxis;
   double[] position;
   double[] orientation;
   
   double glbMass;
   double glbLength;
   String glbAngle;

   public Bone(String n, boolean t)
   {
      name = n;
      root = t;
   }

   public Bone(int i, String n, double[] d, double l, double[] a)
   {
      id = i;
      name = n;
      direction = d;
      currentDir = copyArray(direction);
      length = l;
      axis = a;
      root = false;
   }

   public Bone(int i, String n, double[] d, double l, double[] a, String df, double[][] lts)
   {
      id = i;
      name = n;
      direction = d;
      currentDir = copyArray(direction);
      length = l;
      axis = a;
      dof = df;
      limits = lts;
      root = false;
   }
   
   public double[][] getLimits()
   {
       return this.limits;
   }

   public void setGlobalMass(double m)
   {
      glbMass = m;
   }

   public void setGlobalLength(double l)
   {
      glbLength = l;
   }

   public void setGlobalAngle(String a)
   {
      glbAngle = a;
   }

   public void setupRoot(String ord, String a, double[] p, double[] o)
   {
      order = ord;
      rootAxis = a;
      position = p;
      orientation = o;
   }

   public boolean isRoot()
   {
      return root;
   }

      
   public Vector3D getAbsPosition()
   {
       if (parent != null)
       {
           return parent.getAbsPositionFromTree();
       }
       else
           return Vector3D.zero();
   }

   public Vector3D getRelPosition()
   {
       if (parent != null)
       {
           return this.parent.getDir().unit().times((float)this.parent.length);
       }
       else
           return Vector3D.zero();
   }

   public Vector3D getAbsPositionFromTree()
   {
       Vector3D parentPos = Vector3D.zero();

       if (parent != null)
       {
           parentPos = parent.getAbsPositionFromTree();
       }

       Vector3D myPosition = parentPos.add(this.getDir().unit().times((float)this.length));
       return myPosition;
   }

   //Takes the current direction and a new direction and
   //returns the angle between them. The axis of rotation
   //will be perpendicular to the start and end vectors
   public Rotation getRelRotationFromDir(Vector3D newDir)
   {
       Vector3D a = this.getDir().unit();
       Vector3D b = newDir.unit();
       float angle = (float)Math.acos(a.dot(b));
       Vector3D axis = a.cross(b).unit();
       return new Rotation(angle,axis);
   }

   //Takes (0,-1,0) as current direction and calcs rotation
   //to this bone's direction vector
   public Rotation getRotation()
   {
       Vector3D a = new Vector3D(0,-1,0).unit();
       Vector3D b = this.getDir().unit();
       float angle = (float)Math.toDegrees(Math.acos((float)a.dot(b)));
       Vector3D axis = a.cross(b).unit();
       return new Rotation(angle,axis);
   }

   public void print()
   {
       if (direction != null)
       {
           System.out.println("BONE Name: " + this.name + ", ID: " + id
           + ", Dir: (" + direction[0] + ", " + direction[1] + ", " +
           direction[2] + "), Length: " + length + ", " + this.childString());
       }
       else
       {
           System.out.println("BONE Name: " + this.name + ", ID: " + id
           + ", Length: " + length + ", " + this.childString());
       }
           
   }

   private String childString()
   {
       String s = "Children: (";

       if (this.children != null)
       {
           for (int i = 0; i < this.children.size()-1; i++)
           {
               s+= this.children.get(i).name + ", ";
           }
           s+= this.children.get(this.children.size()-1).name + ")";
           return s;
       }
       else
           return (s + "<none>)");
   }

   public String getName()
   {
       return this.name;
   }

   public float getLength()
   {
       return (float)this.length;
   }

   public void setName(String n)
   {
       this.name = n;
   }

   public Vector3D getDir()
   {
       if (this.direction != null)
       {
           return new Vector3D((float)direction[0],
           (float)direction[1],(float)direction[2]);
       }
       else
           return Vector3D.zero();
   }

   public void resetDir()
   {
       currentDir = copyArray(direction); 
   }

   double[] copyArray(double[] in)
   {
       double[] out = null;
       if (in == null)
       {
           return null;
       }
       else
       {
           out = new double[in.length];
           for (int i = 0; i < in.length; i++)
           {
               out[i] = in[i];
           }
           return out;
       }
   }

   public double[] getAxis()
   {
       return this.axis;
   }

}
